package cc.autoapi.pucong.auto.flow.provider.api.manager;


import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.api.design.ApiNodeParamsVo;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.api.design.ApiNodeRelationVo;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.api.design.ApiNodeVo;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.node.TaskNode;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.node.TaskNodeParamIn;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.node.TaskNodeRelation;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.task.ApiExecResult;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.exception.AutoDesignException;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.exec.ProcessExecStart;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.selfenum.NodeTypeEnum;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.selfenum.ParamPositionEnum;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.selfenum.RunStatusEnum;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.utils.FlowDesignUtils;
import cc.autoapi.pucong.auto.flow.core.root.entity.UserJwt;
import cc.autoapi.pucong.auto.flow.core.root.exception.AutoFlowException;
import cc.autoapi.pucong.auto.flow.core.root.selfenum.ApiTypeEnum;
import cc.autoapi.pucong.auto.flow.core.root.selfenum.CommonBoolenEnum;
import cc.autoapi.pucong.auto.flow.core.root.utils.ApplicationContextUtil;
import cc.autoapi.pucong.auto.flow.core.root.utils.FileUtil;
import cc.autoapi.pucong.auto.flow.core.root.utils.UUID;
import cc.autoapi.pucong.auto.flow.persistence.po.api.ApiInfoPo;
import cc.autoapi.pucong.auto.flow.persistence.po.api.ApiParamsPo;
import cc.autoapi.pucong.auto.flow.persistence.po.sys.GroupInfoPo;
import cc.autoapi.pucong.auto.flow.persistence.repository.api.ApiInfoService;
import cc.autoapi.pucong.auto.flow.persistence.repository.api.ApiNodeService;
import cc.autoapi.pucong.auto.flow.persistence.repository.api.ApiParamsService;
import cc.autoapi.pucong.auto.flow.persistence.repository.api.AutoFlowAuthInterceptor;
import cc.autoapi.pucong.auto.flow.persistence.repository.log.ApiInstanceNodeService;
import cc.autoapi.pucong.auto.flow.persistence.repository.log.ApiInstanceService;
import cc.autoapi.pucong.auto.flow.persistence.utils.UserRequest;
import cc.autoapi.pucong.auto.flow.provider.api.entity.ApiInfoConvert;
import cc.autoapi.pucong.auto.flow.provider.api.entity.dto.ApiInfoAddDto;
import cc.autoapi.pucong.auto.flow.provider.api.entity.dto.ApiInfoTestExecDto;
import cc.autoapi.pucong.auto.flow.provider.api.entity.dto.ApiInfoUpdateDesignDto;
import cc.autoapi.pucong.auto.flow.provider.api.entity.dto.ApiInfoUpdateDto;
import cc.autoapi.pucong.auto.flow.provider.api.entity.qo.ApiInfoQo;
import cc.autoapi.pucong.auto.flow.provider.api.entity.vo.ApiInfoVo;
import cc.autoapi.pucong.auto.flow.provider.api.entity.vo.ApiParamsAddVo;
import cc.autoapi.pucong.auto.flow.provider.api.entity.vo.ApiTestExecResult;
import cc.autoapi.pucong.auto.flow.provider.api.entity.vo.ApiTreeVo;
import cc.autoapi.pucong.auto.flow.provider.api.utils.ApiDesignUtils;
import cc.autoapi.pucong.auto.flow.provider.group.service.GroupInfoService;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author pucong
 * @description 接口信息 managerService 实现类
 * @create 2024-09-24
 */
@Service
@Slf4j
public class ApiInfoManager {

    @Resource
    private ApiInfoService apiInfoService;

    @Resource
    private ApiParamsService apiParamsService;

    @Resource
    private ApiNodeService apiNodeService;

    @Resource
    private GroupInfoService groupInfoService;

    @Resource
    private ApiInstanceService apiInstanceService;

    @Resource
    private ApiInstanceNodeService apiInstanceNodeService;

    @Autowired
    private AutoFlowAuthInterceptor autoFlowAuthInterceptor;

    public List<ApiTreeVo> getList(ApiInfoQo qo) {
        //接口名称
        List<ApiInfoPo> apiInfos = apiInfoService.list(qo.getApiName(), qo.getGroupId(), CommonBoolenEnum.getItemByValue(qo.getApiStatus()));
        // 过滤数据
        apiInfos = autoFlowAuthInterceptor.fitterApiList(apiInfos);
        List<ApiTreeVo> apiInfoVos = new ArrayList<>();
        // 获取分组信息
        if (apiInfos.size() > 0) {
            List<String> groupIds = apiInfos.stream().map(ApiInfoPo::getGroupId).collect(Collectors.toList());
            List<GroupInfoPo> groupInfos = groupInfoService.listByIds(groupIds);
            for (GroupInfoPo groupInfo : groupInfos) {
                ApiTreeVo vo = new ApiTreeVo();
                vo.setUuid(groupInfo.getUuid());
                vo.setNodeName(groupInfo.getName());
                vo.setTreeType(0);
                vo.setParentId("-1");
                vo.setWhetherEnable(CommonBoolenEnum.是);
                vo.setSortIndex(groupInfo.getSortIndex());
                apiInfoVos.add(vo);
            }
            for (ApiInfoPo nodeInfo : apiInfos) {
                ApiTreeVo vo = new ApiTreeVo();
                vo.setUuid(nodeInfo.getUuid());
                vo.setNodeName(nodeInfo.getApiName());
                vo.setNodeCode(nodeInfo.getApiCode());
                vo.setTreeType(1);
                vo.setParentId(nodeInfo.getGroupId());
                vo.setWhetherEnable(nodeInfo.getApiStatus());
                vo.setSortIndex(nodeInfo.getSortIndex());
                apiInfoVos.add(vo);
            }
        }
        return apiInfoVos;
    }

    public ApiInfoVo getDetailById(String id) {
        ApiInfoPo apiInfo = apiInfoService.getById(id);
        ApiInfoVo apiInfoVo = ApiInfoConvert.INSTANCE.po2vo(apiInfo);
        String apiCode = apiInfoVo.getApiCode();
        if (StringUtils.isNotBlank(apiCode)) {
            String url = "/api/exec/apiExec";
            if (apiCode.startsWith("/")) {
                url += apiCode;
            } else {
                url += "/" + apiCode;
            }
            apiInfoVo.setUrl(url);
        }
        return apiInfoVo;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public String add(ApiInfoAddDto dto) {
        ApiInfoPo apiInfo = ApiInfoConvert.INSTANCE.dto2po(dto);
        String id = UUID.uuid();
        apiInfo.setUuid(id);
        apiInfo.setApiStatus(CommonBoolenEnum.否);
        apiInfo.setCreateBy(UserRequest.getLoginUser().getUuid());
        apiInfo.setCreateTime(new Date());
        Boolean i = apiInfoService.save(apiInfo);
        return id;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Boolean updateById(String id, ApiInfoUpdateDto dto) {
        ApiInfoPo apiInfoUpdate = ApiInfoConvert.INSTANCE.dto2po(dto);
        apiInfoUpdate.setUuid(id);
        Boolean i = apiInfoService.updateById(apiInfoUpdate);
        return i;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Boolean batchDeleteByIds(List<String> ids) {
        Boolean i = apiInfoService.removeBatchByIds(ids);

        // 日志信息
        apiInstanceService.deleteByApiId(ids);
        apiInstanceNodeService.deleteByApiId(ids);
        return i;
    }

    public Boolean enabledById(String apiId) {
        ApiInfoPo byId = apiInfoService.getById(apiId);
        if (byId == null) {
            throw new AutoFlowException("传入参数错误");
        }
        List<ApiNodeRelationVo> addRelateList = new ArrayList<>();
        List<ApiNodeVo> addNodeList = new ArrayList<>();
        List<ApiNodeParamsVo> apiNodeParamsVoList = new ArrayList<>();
        // 解析json
        FlowDesignUtils.dealJson2List(byId.getApiJson(),
                addNodeList, addRelateList,
                apiNodeParamsVoList);

        // 校验内容是否必填
        List<TaskNode> taskNodeList = apiNodeService.getTaskNodeList(addNodeList, apiNodeParamsVoList);
        List<TaskNodeRelation> taskNodeRelationList = apiNodeService.getTaskNodeRelationList(addRelateList);
        // 起始节点只能是api和cron
        if (!checkStartNode(taskNodeList, taskNodeRelationList)) {
            throw new AutoFlowException("起始节点有且只能有一个api或cron");
        }
        if (byId.getServiceType() == 0) {
            String apiCode = byId.getApiCode();
            ApiTypeEnum apiType = byId.getApiType();
            if (StringUtils.isBlank(apiCode) || apiType == null) {
                throw new AutoFlowException("请配置API信息，api组件的编码和apiType不能为空");
            }
        }
        checkParamsOneStart(taskNodeList);
        // 必填参数是否设置完成
        checkParams(taskNodeList);

        ApiInfoPo apiInfoUpdate = new ApiInfoPo();
        apiInfoUpdate.setUuid(apiId);
        apiInfoUpdate.setApiStatus(CommonBoolenEnum.是);
        Boolean i = apiInfoService.updateById(apiInfoUpdate);
        // 发布缓存
        apiInfoService.publishApi(apiId);
        return i;
    }


    private void checkParams(List<TaskNode> taskNodeList) {
        for (TaskNode taskNode : taskNodeList) {
            NodeTypeEnum nodeType = NodeTypeEnum.getItemByValue(taskNode.getNodeType());
            if (nodeType != null) {
                if (nodeType == NodeTypeEnum.JAVA_SCRIPT ||
                        nodeType == NodeTypeEnum.JS_SCRIPT ||
                        nodeType == NodeTypeEnum.PYTHON_SCRIPT) {
                    String nodeScript = taskNode.getNodeScript();
                    if (StringUtils.isBlank(nodeScript)) {
                        throw new AutoFlowException(String.format("脚本内容不能为空，节点名称：%s", taskNode.getNodeName()));
                    }
                }
            }
            List<TaskNodeParamIn> taskNodeParamInList = taskNode.getTaskNodeParamInList();
            if (nodeType == null || nodeType != NodeTypeEnum.API_CONFIG) {
                for (TaskNodeParamIn taskNodeParamIn : taskNodeParamInList) {
                    String paramCode = taskNodeParamIn.getParamCode();
                    Boolean needValue = taskNodeParamIn.getNeedValue();
                    String paramValue = taskNodeParamIn.getParamValue();
                    if (needValue && StringUtils.isBlank(paramValue)) {
                        throw new AutoFlowException(String.format("组件必填内容为空，节点名称：%s，参数编码：%s", taskNode.getNodeName(), paramCode));
                    }
                }
            }
        }
    }

    private boolean checkStartNode(List<TaskNode> taskNodeList, List<TaskNodeRelation> taskNodeRelationList) {
        List<TaskNode> startNodes = new ArrayList<>();
        Map<String, List<TaskNodeRelation>> taskNodeRelationMap = taskNodeRelationList.stream().collect(Collectors.groupingBy(TaskNodeRelation::getNodeToId));

        for (TaskNode taskNode : taskNodeList) {
            List<TaskNodeRelation> taskNodeRelations = taskNodeRelationMap.get(taskNode.getUuid());
            if (taskNodeRelations != null && taskNodeRelations.size() > 0) {
                continue;
            }
            startNodes.add(taskNode);
        }
        if (startNodes.size() == 1) {
            return true;
        }
        return false;
    }

    private void checkParamsOneStart(List<TaskNode> taskNodeList) {
        int apiCount = 0;
        int cronCount = 0;
        for (TaskNode execNode : taskNodeList) {
            NodeTypeEnum nodeType = NodeTypeEnum.getItemByValue(execNode.getNodeType());
            if (nodeType != null) {
                if (nodeType == NodeTypeEnum.API_CONFIG) {
                    apiCount++;
                }
                if (nodeType == NodeTypeEnum.CRON_CONFIG) {
                    cronCount++;
                }
            }
        }
        if (apiCount > 0 && cronCount > 0) {
            throw new AutoDesignException("节点中不能同时存在api节点和定时节点");
        }
        if (apiCount > 1 || cronCount > 1) {
            throw new AutoDesignException("节点中只能存在一个api节点或定时节点");
        }
        if (apiCount == 0 && cronCount == 0) {
            throw new AutoDesignException("节点中必须存在api节点或定时节点");
        }
    }

    public Boolean disabledById(String apiId) {
        ApiInfoPo apiInfoUpdate = new ApiInfoPo();
        apiInfoUpdate.setUuid(apiId);
        apiInfoUpdate.setApiStatus(CommonBoolenEnum.否);
        Boolean i = apiInfoService.updateById(apiInfoUpdate);
        // 删除缓存
        apiInfoService.removeApi(apiId);
        return i;
    }

    public Boolean updateDesignById(String id, ApiInfoUpdateDesignDto dto) {
        ApiInfoPo byId = apiInfoService.getById(id);
        if (byId.getApiJson() != null && byId.getApiJson().equals(dto.getApiJson())) {
            return true;
        }
        ApiInfoPo apiInfoUpdate = new ApiInfoPo();
        apiInfoUpdate.setUuid(id);
        apiInfoUpdate.setApiJson(dto.getApiJson());

        List<ApiParamsAddVo> apiParamsAdds = new ArrayList<>();
        // 解析json
        ApiDesignUtils.dealJson2List(dto.getApiJson(), apiInfoUpdate, apiParamsAdds);
        // 更新信息
        Boolean i = apiInfoService.updateById(apiInfoUpdate);

        apiParamsService.remove(id);

        saveBatchNodeInfo(id, apiParamsAdds);
        return i;
    }

    public String copyApiByApiId(String apiId) {
        UserJwt loginUser = UserRequest.getLoginUser();
        Map<String, String> map = new HashMap<>(16);
        ApiInfoPo apiInfoPo = apiInfoService.getById(apiId);
        String apiJson = apiInfoPo.getApiJson();

        List<ApiParamsPo> projectApiParams = apiParamsService.list(apiId);

        List<String> jsonIdList = FlowDesignUtils.getJsonIdList(apiJson);
        jsonIdList.add(apiId);

        jsonIdList = new ArrayList<>(new HashSet<>(jsonIdList));
        for (String s : jsonIdList) {
            map.put(s, UUID.uuid());
        }
        for (String s : jsonIdList) {
            String newId = map.get(s);
            apiJson = apiJson.replaceAll(s, newId);
        }
        String uuid = UUID.uuid();
        apiInfoPo.setUuid(uuid);
        apiInfoPo.setApiName(apiInfoPo.getApiName() + " 副本");
        apiInfoPo.setApiCode(apiInfoPo.getApiCode() + "repeat");
        apiInfoPo.setCreateTime(new Date());
        apiInfoPo.setCreateBy(loginUser.getUuid());
        apiInfoPo.setApiStatus(CommonBoolenEnum.否);
        apiInfoPo.setApiJson(apiJson);

        for (ApiParamsPo projectApiParam : projectApiParams) {
            String newId = map.get(projectApiParam.getUuid());
            if (StringUtils.isBlank(newId)) {
                newId = UUID.uuid();
                map.put(projectApiParam.getUuid(), newId);
            }
            projectApiParam.setUuid(newId);
            projectApiParam.setApiId(uuid);
        }
        // 获取数据
        List<ApiParamsAddVo> apiParamsAdds = new ArrayList<>();
        ApiDesignUtils.dealJson2List(apiJson, apiInfoPo, apiParamsAdds);
        // 保存数据
        apiInfoService.save(apiInfoPo);
        saveBatchNodeInfo(uuid, apiParamsAdds);
        return apiInfoPo.getUuid();
    }

    private void saveBatchNodeInfo(String apiId, List<ApiParamsAddVo> apiParamsAdds) {
        if (apiParamsAdds.size() > 0) {
            List<ApiParamsPo> list = new ArrayList<>();
            for (ApiParamsAddVo vo : apiParamsAdds) {
                ApiParamsPo po = new ApiParamsPo();
                po.setUuid(UUID.uuid());
                po.setApiId(apiId);
                po.setRequestPosition(vo.getRequestPosition());
                po.setParamCode(vo.getParamCode());
                po.setDateType(vo.getDateType());
                po.setParamType(vo.getParamType());
                po.setNeedValue(vo.getNeedValue());
                po.setParamDesc(vo.getParamDesc());
                list.add(po);
            }
            apiParamsService.saveBatch(list);
        }
    }

    public void uploadApiInfo(MultipartFile file) {
        String fileContent = getJsonFileContent(file);

        // 获取json文件
        JSONObject jsonObject = JSON.parseObject(fileContent);

        JSONObject apiInfoPo = jsonObject.getJSONObject("apiInfoPo");
        JSONArray groupInfos = jsonObject.getJSONArray("groupInfos");

        ApiInfoPo apiInfo2 = apiInfoPo.toJavaObject(ApiInfoPo.class);
        apiInfo2.setApiStatus(CommonBoolenEnum.否);
        ApiInfoPo projectApiInfo1 = apiInfoService.getById(apiInfo2.getUuid());
        if (projectApiInfo1 == null) {
            apiInfoService.save(apiInfo2);
        } else {
            apiInfoService.updateById(apiInfo2);
        }

        String apiId = apiInfo2.getUuid();
        String apiJson = apiInfo2.getApiJson();
        List<String> jsonIdList = FlowDesignUtils.getJsonIdList(apiJson);
        jsonIdList.add(apiId);

        Map<String, String> map = new HashMap<>(16);
        jsonIdList = new ArrayList<>(new HashSet<>(jsonIdList));
        for (String s : jsonIdList) {
            map.put(s, UUID.uuid());
        }
        for (String s : jsonIdList) {
            String newId = map.get(s);
            apiJson = apiJson.replaceAll(s, newId);
        }

        // 更新设计数据
        if (StringUtils.isNotBlank(apiInfo2.getApiJson())) {
            ApiInfoUpdateDesignDto designDto = new ApiInfoUpdateDesignDto();
            designDto.setApiJson(apiInfo2.getApiJson());
            updateDesignById(apiInfo2.getUuid(), designDto);
        }

        for (Object groupInfo : groupInfos) {
            JSONObject groupInfo2 = (JSONObject) groupInfo;
            GroupInfoPo groupInfo3 = groupInfo2.toJavaObject(GroupInfoPo.class);
            GroupInfoPo groupInfo1 = groupInfoService.getById(groupInfo3.getUuid());
            if (groupInfo1 == null) {
                groupInfoService.save(groupInfo3);
            } else {
                groupInfoService.updateById(groupInfo3);
            }
        }
    }

    public String getJsonFileContent(MultipartFile file) {
        String content = "";
        try {
            byte[] data = file.getBytes();
            content = new String(data, StandardCharsets.UTF_8);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return content;
    }


    public void exportApiByApiId(String apiId, HttpServletResponse response) {
        JSONObject vo = new JSONObject();

        ApiInfoPo apiInfoPo = apiInfoService.getById(apiId);
        if (apiInfoPo == null) {
            throw new AutoFlowException("传入参数错误");
        }
        vo.put("apiInfoPo", apiInfoPo);
        List<String> groupIds = new ArrayList<>();
        groupIds.add(apiInfoPo.getGroupId());

        List<GroupInfoPo> groupInfos = groupInfoService.listByIds(groupIds);
        vo.put("groupInfos", groupInfos);

        String basicPath = ApplicationContextUtil.getTmpPath() + File.separatorChar + UUID.uuid();
        String filePath = basicPath + File.separatorChar + "api.json";
        FileUtil.writeToFile(filePath, vo);

        // 把文字内容导出
        FileUtil.downLoadFile(filePath, "api_" + apiInfoPo.getApiName() + ".json", response);
        // 删除文件
        FileUtil.deleteFile(filePath);
    }

    public ApiTestExecResult testExecById(String apiId, ApiInfoTestExecDto dto) {
        Date date = new Date();
        ApiInfoPo byId = apiInfoService.getById(apiId);
        // 未发布则清除缓存
        if (byId.getApiStatus() == CommonBoolenEnum.否) {
            apiInfoService.removeApi(apiId);
        }
        JSONObject params = dto.getParams();
        // 初始化任务节点
        apiInfoService.publishApi(apiId);
        List<ApiParamsPo> paramInList = apiParamsService.list(apiId, 0);

        Map<String, Object> queryMap = new HashMap<>(16);
        Map<String, String> headerMap = new HashMap<>(16);
        Map<String, Object> bodyMap = new HashMap<>(16);
        for (ApiParamsPo apiParamsPo : paramInList) {
            String paramCode = apiParamsPo.getParamCode();
            Object dateValue1 = params.get(paramCode);
            ParamPositionEnum paramPosition = ParamPositionEnum.getItemByValue(apiParamsPo.getRequestPosition());
            switch (paramPosition) {
                case QUERY:
                    queryMap.put(paramCode, dateValue1);
                    break;
                case BODY:
                    bodyMap.put(paramCode, dateValue1);
                    break;
                case HEADER:
                    headerMap.put(paramCode, dateValue1 == null ? null : dateValue1.toString());
                    break;
                default:
                    throw new AutoDesignException("传入参数位置错误");
            }
        }
        // 创建实例信息
        String instanceId = UUID.uuid();
        // 开始执行
        ProcessExecStart processExecStart = new ProcessExecStart(apiId, instanceId, queryMap, headerMap, bodyMap);
        ApiTestExecResult result = new ApiTestExecResult();
        ApiExecResult execResult = null;
        try {
            execResult = processExecStart.start();
        } catch (Exception e) {
            String stackTrace = ExceptionUtils.getStackTrace(e);
            log.error("执行服务异常: ", e);
            result.setErrorLog(stackTrace);
            result.setRunSuccess(false);
        }
        if (execResult != null) {
            result.setErrorLog(execResult.getErrorLog());
            result.setRunSuccess(execResult.getRunStatus() == RunStatusEnum.执行成功);
            result.setData(execResult.getData());
        }
        // 未发布则清除缓存
        if (byId.getApiStatus() == CommonBoolenEnum.否) {
            // 清理缓存,延迟3秒执行
            Timer timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    apiInfoService.removeApi(apiId);
                }
            }, 3000);
        }
        result.setTime(new Date().getTime() - date.getTime());
        return result;
    }
}
